#ifndef Singleton_h
#define Singleton_h

#include <stdio.h>
#include <map>
#include <assert.h>
#include <iostream>
using namespace std;

/**
  * Simple singleton implementation.
  */
template<class T>
class Singleton {
  public:
    static inline T* getInstance();
    static inline void kill();

  private:
    static T* instance_;
};

template<class T>
T* Singleton<T>::
getInstance () {
  if (instance_ == NULL) {
    instance_ = new T;
  }
  return instance_;
}

template<class T>
void Singleton<T>::
kill () {
  delete instance_;
  instance_ = NULL;
}

template<class T>
T* Singleton<T>::instance_ = NULL;


/**
  * Session singleton: one instance per
  * session. A session is identified
  * uniquely with the adress of a session object.
  *
  * A typical use is given in the following example:
  *
  * RootWindow* rootwin1 = new RootWindow;
  * SubWindowSingleton* subwin1 = SubWindowSingleton::getInstance(rootwin1);
  * RootWindow* rootwin2 = new RootWindow;
  * SubWindowSingleton* subwin2 = SubWindowSingleton::getInstance(rootwin2);
  *
  * Here, 2 sessions are managed with 2 distinct singletons which are subwin1
  * and subwin2, attached respectively to the session objects rootwin1 and
  * rootwin2.
  */
template<class T>
class SessionSingleton {
  public:
    static inline T* getInstance(void* session_object);
    static inline void kill(void* session_object);
    /**
      * Given an instance T, retrieve the key used
      * to construct it.
      * WARNING: Never call this function from a constructor
      * of class T as the constructor is called from within
      * 'getInstance(void*)' and the association with the key
      * can only be made *after* the instance has been built.
      */
    inline void* getInstanceKey() const;

  private:
    static map<void*,T*> instances_;
};

template<class T>
T* SessionSingleton<T>::
getInstance(void* session_object) {
  assert(session_object != NULL);
  T* instance = NULL;
  typename map<void*,T*>::iterator it = instances_.find(session_object);
  if (it == instances_.end()) {
    instance = instances_[session_object] = new T;
  }
  else {
    instance = it->second;
  }
  return instance;
}

template<class T>
void SessionSingleton<T>::
kill(void* session_object) {
  assert(session_object != NULL);
  typename map<void*,T*>::iterator it = instances_.find(session_object);
  if (it != instances_.end()) {
    instances_.erase(it);
    delete it->second;
  }
}

template<class T>
void* SessionSingleton<T>::
getInstanceKey() const {
  typename map<void*,T*>::iterator it = instances_.begin();
  for (; it != instances_.end(); ++it) {
    if (it->second == this) {
      return it->first;
    }
  }
  return NULL;
}

template<class T>
map<void*,T*> SessionSingleton<T>::instances_;

#endif

